import assert from "node:assert";
import { test, describe } from "vitest";
import { fixJson } from "./fixJson";

test("should handle empty input", () => {
  assert.strictEqual(fixJson(""), "");
});

describe("literals", () => {
  test("should handle incomplete null", () => {
    assert.strictEqual(fixJson("nul"), "null");
  });

  test("should handle incomplete true", () => {
    assert.strictEqual(fixJson("t"), "true");
  });

  test("should handle incomplete false", () => {
    assert.strictEqual(fixJson("fals"), "false");
  });
});

describe("number", () => {
  test("should handle incomplete numbers", () => {
    assert.strictEqual(fixJson("12."), "12");
  });

  test("should handle numbers with dot", () => {
    assert.strictEqual(fixJson("12.2"), "12.2");
  });

  test("should handle negative numbers", () => {
    assert.strictEqual(fixJson("-12"), "-12");
  });

  test("should handle incomplete negative numbers", () => {
    assert.strictEqual(fixJson("-"), "");
  });

  test("should handle e-notation numbers", () => {
    assert.strictEqual(fixJson("2.5e"), "2.5");
    assert.strictEqual(fixJson("2.5e-"), "2.5");
    assert.strictEqual(fixJson("2.5e3"), "2.5e3");
    assert.strictEqual(fixJson("-2.5e3"), "-2.5e3");
  });

  test("should handle uppercase e-notation numbers", () => {
    assert.strictEqual(fixJson("2.5E"), "2.5");
    assert.strictEqual(fixJson("2.5E-"), "2.5");
    assert.strictEqual(fixJson("2.5E3"), "2.5E3");
    assert.strictEqual(fixJson("-2.5E3"), "-2.5E3");
  });

  test("should handle incomplete numbers", () => {
    assert.strictEqual(fixJson("12.e"), "12");
    assert.strictEqual(fixJson("12.34e"), "12.34");
    assert.strictEqual(fixJson("5e"), "5");
  });
});

describe("string", () => {
  test("should handle incomplete strings", () => {
    assert.strictEqual(fixJson('"abc'), '"abc"');
  });

  test("should handle escape sequences", () => {
    assert.strictEqual(
      fixJson('"value with \\"quoted\\" text and \\\\ escape'),
      '"value with \\"quoted\\" text and \\\\ escape"'
    );
  });

  test("should handle incomplete escape sequences", () => {
    assert.strictEqual(fixJson('"value with \\'), '"value with "');
  });

  test("should handle unicode characters", () => {
    assert.strictEqual(
      fixJson('"value with unicode \u003C"'),
      '"value with unicode \u003C"'
    );
  });
});

describe("array", () => {
  test("should handle incomplete array", () => {
    assert.strictEqual(fixJson("["), "[]");
  });

  test("should handle closing bracket after number in array", () => {
    assert.strictEqual(fixJson("[[1], [2"), "[[1], [2]]");
  });

  test("should handle closing bracket after string in array", () => {
    assert.strictEqual(fixJson(`[["1"], ["2`), `[["1"], ["2"]]`);
  });

  test("should handle closing bracket after literal in array", () => {
    assert.strictEqual(fixJson("[[false], [nu"), "[[false], [null]]");
  });

  test("should handle closing bracket after array in array", () => {
    assert.strictEqual(fixJson("[[[]], [[]"), "[[[]], [[]]]");
  });

  test("should handle closing bracket after object in array", () => {
    assert.strictEqual(fixJson("[[{}], [{"), "[[{}], [{}]]");
  });

  test("should handle trailing comma", () => {
    assert.strictEqual(fixJson("[1, "), "[1]");
  });

  test("should handle closing array", () => {
    assert.strictEqual(fixJson("[[], 123"), "[[], 123]");
  });
});

describe("object", () => {
  test("should handle keys without values", () => {
    assert.strictEqual(fixJson('{"key":'), "{}");
  });

  test("should handle closing brace after number in object", () => {
    assert.strictEqual(
      fixJson('{"a": {"b": 1}, "c": {"d": 2'),
      '{"a": {"b": 1}, "c": {"d": 2}}'
    );
  });

  test("should handle closing brace after string in object", () => {
    assert.strictEqual(
      fixJson('{"a": {"b": "1"}, "c": {"d": 2'),
      '{"a": {"b": "1"}, "c": {"d": 2}}'
    );
  });

  test("should handle closing brace after literal in object", () => {
    assert.strictEqual(
      fixJson('{"a": {"b": false}, "c": {"d": 2'),
      '{"a": {"b": false}, "c": {"d": 2}}'
    );
  });

  test("should handle closing brace after array in object", () => {
    assert.strictEqual(
      fixJson('{"a": {"b": []}, "c": {"d": 2'),
      '{"a": {"b": []}, "c": {"d": 2}}'
    );
  });

  test("should handle closing brace after object in object", () => {
    assert.strictEqual(
      fixJson('{"a": {"b": {}}, "c": {"d": 2'),
      '{"a": {"b": {}}, "c": {"d": 2}}'
    );
  });

  test("should handle partial keys (first key)", () => {
    assert.strictEqual(fixJson('{"ke'), "{}");
  });

  test("should handle partial keys (second key)", () => {
    assert.strictEqual(fixJson('{"k1": 1, "k2'), '{"k1": 1}');
  });

  test("should handle partial keys with colon (second key)", () => {
    assert.strictEqual(fixJson('{"k1": 1, "k2":'), '{"k1": 1}');
  });

  test("should handle trailing whitespaces", () => {
    assert.strictEqual(fixJson('{"key": "value"  '), '{"key": "value"}');
  });
});

describe("nesting", () => {
  test("should handle nested arrays with numbers", () => {
    assert.strictEqual(fixJson("[1, [2, 3, ["), "[1, [2, 3, []]]");
  });

  test("should handle nested arrays with literals", () => {
    assert.strictEqual(fixJson("[false, [true, ["), "[false, [true, []]]");
  });

  test("should handle nested objects", () => {
    assert.strictEqual(fixJson('{"key": {"subKey":'), '{"key": {}}');
  });

  test("should handle nested objects with numbers", () => {
    assert.strictEqual(
      fixJson('{"key": 123, "key2": {"subKey":'),
      '{"key": 123, "key2": {}}'
    );
  });

  test("should handle nested objects with literals", () => {
    assert.strictEqual(
      fixJson('{"key": null, "key2": {"subKey":'),
      '{"key": null, "key2": {}}'
    );
  });

  test("should handle arrays within objects", () => {
    assert.strictEqual(fixJson('{"key": [1, 2, {'), '{"key": [1, 2, {}]}');
  });

  test("should handle objects within arrays", () => {
    assert.strictEqual(
      fixJson('[1, 2, {"key": "value",'),
      '[1, 2, {"key": "value"}]'
    );
  });

  test("should handle nested arrays and objects", () => {
    assert.strictEqual(
      fixJson('{"a": {"b": ["c", {"d": "e",'),
      '{"a": {"b": ["c", {"d": "e"}]}}'
    );
  });

  test("should handle deeply nested objects", () => {
    assert.strictEqual(
      fixJson('{"a": {"b": {"c": {"d":'),
      '{"a": {"b": {"c": {}}}}'
    );
  });

  test("should handle potential nested arrays or objects", () => {
    assert.strictEqual(fixJson('{"a": 1, "b": ['), '{"a": 1, "b": []}');
    assert.strictEqual(fixJson('{"a": 1, "b": {'), '{"a": 1, "b": {}}');
    assert.strictEqual(fixJson('{"a": 1, "b": "'), '{"a": 1, "b": ""}');
  });
});

describe("regression", () => {
  test("should handle complex nesting 1", () => {
    assert.strictEqual(
      fixJson(
        [
          "{",
          '  "a": [',
          "    {",
          '      "a1": "v1",',
          '      "a2": "v2",',
          `      "a3": "v3"`,
          "    }",
          "  ],",
          '  "b": [',
          "    {",
          '      "b1": "n',
        ].join("\n")
      ),
      [
        "{",
        '  "a": [',
        "    {",
        '      "a1": "v1",',
        '      "a2": "v2",',
        `      "a3": "v3"`,
        "    }",
        "  ],",
        '  "b": [',
        "    {",
        '      "b1": "n"}]}',
      ].join("\n")
    );
  });
});
